Passed
Pull Request — master (#57)
by
unknown
11:34 queued 08:49
created

dropdown.js ➔ show   C

Complexity

Conditions 11

Size

Total Lines 66
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 31
dl 0
loc 66
rs 5.4
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like dropdown.js ➔ show often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
  * Bootstrap dropdown.js v4.6.1 (https://getbootstrap.com/)
3
  * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
  */
6
(function (global, factory) {
7
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('jquery'), require('popper.js'), require('./util.js')) :
8
  typeof define === 'function' && define.amd ? define(['jquery', 'popper.js', './util'], factory) :
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global.jQuery, global.Popper, global.Util));
10
})(this, (function ($, Popper, Util) { 'use strict';
11
12
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
13
14
  var $__default = /*#__PURE__*/_interopDefaultLegacy($);
15
  var Popper__default = /*#__PURE__*/_interopDefaultLegacy(Popper);
16
  var Util__default = /*#__PURE__*/_interopDefaultLegacy(Util);
17
18
  function _defineProperties(target, props) {
19
    for (var i = 0; i < props.length; i++) {
20
      var descriptor = props[i];
21
      descriptor.enumerable = descriptor.enumerable || false;
22
      descriptor.configurable = true;
23
      if ("value" in descriptor) descriptor.writable = true;
24
      Object.defineProperty(target, descriptor.key, descriptor);
25
    }
26
  }
27
28
  function _createClass(Constructor, protoProps, staticProps) {
29
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
30
    if (staticProps) _defineProperties(Constructor, staticProps);
31
    return Constructor;
32
  }
33
34
  function _extends() {
35
    _extends = Object.assign || function (target) {
36
      for (var i = 1; i < arguments.length; i++) {
37
        var source = arguments[i];
38
39
        for (var key in source) {
40
          if (Object.prototype.hasOwnProperty.call(source, key)) {
41
            target[key] = source[key];
42
          }
43
        }
44
      }
45
46
      return target;
47
    };
48
49
    return _extends.apply(this, arguments);
50
  }
51
52
  /**
53
   * Constants
54
   */
55
56
  var NAME = 'dropdown';
57
  var VERSION = '4.6.1';
58
  var DATA_KEY = 'bs.dropdown';
59
  var EVENT_KEY = "." + DATA_KEY;
60
  var DATA_API_KEY = '.data-api';
61
  var JQUERY_NO_CONFLICT = $__default["default"].fn[NAME];
62
  var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
63
64
  var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key
65
66
  var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key
67
68
  var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
69
70
  var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
71
72
  var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
73
74
  var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE);
75
  var CLASS_NAME_DISABLED = 'disabled';
76
  var CLASS_NAME_SHOW = 'show';
77
  var CLASS_NAME_DROPUP = 'dropup';
78
  var CLASS_NAME_DROPRIGHT = 'dropright';
79
  var CLASS_NAME_DROPLEFT = 'dropleft';
80
  var CLASS_NAME_MENURIGHT = 'dropdown-menu-right';
81
  var CLASS_NAME_POSITION_STATIC = 'position-static';
82
  var EVENT_HIDE = "hide" + EVENT_KEY;
83
  var EVENT_HIDDEN = "hidden" + EVENT_KEY;
84
  var EVENT_SHOW = "show" + EVENT_KEY;
85
  var EVENT_SHOWN = "shown" + EVENT_KEY;
86
  var EVENT_CLICK = "click" + EVENT_KEY;
87
  var EVENT_CLICK_DATA_API = "click" + EVENT_KEY + DATA_API_KEY;
88
  var EVENT_KEYDOWN_DATA_API = "keydown" + EVENT_KEY + DATA_API_KEY;
89
  var EVENT_KEYUP_DATA_API = "keyup" + EVENT_KEY + DATA_API_KEY;
90
  var SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]';
91
  var SELECTOR_FORM_CHILD = '.dropdown form';
92
  var SELECTOR_MENU = '.dropdown-menu';
93
  var SELECTOR_NAVBAR_NAV = '.navbar-nav';
94
  var SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';
95
  var PLACEMENT_TOP = 'top-start';
96
  var PLACEMENT_TOPEND = 'top-end';
97
  var PLACEMENT_BOTTOM = 'bottom-start';
98
  var PLACEMENT_BOTTOMEND = 'bottom-end';
99
  var PLACEMENT_RIGHT = 'right-start';
100
  var PLACEMENT_LEFT = 'left-start';
101
  var Default = {
102
    offset: 0,
103
    flip: true,
104
    boundary: 'scrollParent',
105
    reference: 'toggle',
106
    display: 'dynamic',
107
    popperConfig: null
108
  };
109
  var DefaultType = {
110
    offset: '(number|string|function)',
111
    flip: 'boolean',
112
    boundary: '(string|element)',
113
    reference: '(string|element)',
114
    display: 'string',
115
    popperConfig: '(null|object)'
116
  };
117
  /**
118
   * Class definition
119
   */
120
121
  var Dropdown = /*#__PURE__*/function () {
122
    function Dropdown(element, config) {
123
      this._element = element;
124
      this._popper = null;
125
      this._config = this._getConfig(config);
126
      this._menu = this._getMenuElement();
127
      this._inNavbar = this._detectNavbar();
128
129
      this._addEventListeners();
130
    } // Getters
131
132
133
    var _proto = Dropdown.prototype;
134
135
    // Public
136
    _proto.toggle = function toggle() {
137
      if (this._element.disabled || $__default["default"](this._element).hasClass(CLASS_NAME_DISABLED)) {
138
        return;
139
      }
140
141
      var isActive = $__default["default"](this._menu).hasClass(CLASS_NAME_SHOW);
142
143
      Dropdown._clearMenus();
144
145
      if (isActive) {
146
        return;
147
      }
148
149
      this.show(true);
150
    };
151
152
    _proto.show = function show(usePopper) {
153
      if (usePopper === void 0) {
154
        usePopper = false;
155
      }
156
157
      if (this._element.disabled || $__default["default"](this._element).hasClass(CLASS_NAME_DISABLED) || $__default["default"](this._menu).hasClass(CLASS_NAME_SHOW)) {
158
        return;
159
      }
160
161
      var relatedTarget = {
162
        relatedTarget: this._element
163
      };
164
      var showEvent = $__default["default"].Event(EVENT_SHOW, relatedTarget);
165
166
      var parent = Dropdown._getParentFromElement(this._element);
167
168
      $__default["default"](parent).trigger(showEvent);
169
170
      if (showEvent.isDefaultPrevented()) {
171
        return;
172
      } // Totally disable Popper for Dropdowns in Navbar
173
174
175
      if (!this._inNavbar && usePopper) {
176
        // Check for Popper dependency
177
        if (typeof Popper__default["default"] === 'undefined') {
178
          throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)');
179
        }
180
181
        var referenceElement = this._element;
182
183
        if (this._config.reference === 'parent') {
184
          referenceElement = parent;
185
        } else if (Util__default["default"].isElement(this._config.reference)) {
186
          referenceElement = this._config.reference; // Check if it's jQuery element
187
188
          if (typeof this._config.reference.jquery !== 'undefined') {
189
            referenceElement = this._config.reference[0];
190
          }
191
        } // If boundary is not `scrollParent`, then set position to `static`
192
        // to allow the menu to "escape" the scroll parent's boundaries
193
        // https://github.com/twbs/bootstrap/issues/24251
194
195
196
        if (this._config.boundary !== 'scrollParent') {
197
          $__default["default"](parent).addClass(CLASS_NAME_POSITION_STATIC);
198
        }
199
200
        this._popper = new Popper__default["default"](referenceElement, this._menu, this._getPopperConfig());
201
      } // If this is a touch-enabled device we add extra
202
      // empty mouseover listeners to the body's immediate children;
203
      // only needed because of broken event delegation on iOS
204
      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
205
206
207
      if ('ontouchstart' in document.documentElement && $__default["default"](parent).closest(SELECTOR_NAVBAR_NAV).length === 0) {
208
        $__default["default"](document.body).children().on('mouseover', null, $__default["default"].noop);
209
      }
210
211
      this._element.focus();
212
213
      this._element.setAttribute('aria-expanded', true);
214
215
      $__default["default"](this._menu).toggleClass(CLASS_NAME_SHOW);
216
      $__default["default"](parent).toggleClass(CLASS_NAME_SHOW).trigger($__default["default"].Event(EVENT_SHOWN, relatedTarget));
217
    };
218
219
    _proto.hide = function hide() {
220
      if (this._element.disabled || $__default["default"](this._element).hasClass(CLASS_NAME_DISABLED) || !$__default["default"](this._menu).hasClass(CLASS_NAME_SHOW)) {
221
        return;
222
      }
223
224
      var relatedTarget = {
225
        relatedTarget: this._element
226
      };
227
      var hideEvent = $__default["default"].Event(EVENT_HIDE, relatedTarget);
228
229
      var parent = Dropdown._getParentFromElement(this._element);
230
231
      $__default["default"](parent).trigger(hideEvent);
232
233
      if (hideEvent.isDefaultPrevented()) {
234
        return;
235
      }
236
237
      if (this._popper) {
238
        this._popper.destroy();
239
      }
240
241
      $__default["default"](this._menu).toggleClass(CLASS_NAME_SHOW);
242
      $__default["default"](parent).toggleClass(CLASS_NAME_SHOW).trigger($__default["default"].Event(EVENT_HIDDEN, relatedTarget));
243
    };
244
245
    _proto.dispose = function dispose() {
246
      $__default["default"].removeData(this._element, DATA_KEY);
247
      $__default["default"](this._element).off(EVENT_KEY);
248
      this._element = null;
249
      this._menu = null;
250
251
      if (this._popper !== null) {
252
        this._popper.destroy();
253
254
        this._popper = null;
255
      }
256
    };
257
258
    _proto.update = function update() {
259
      this._inNavbar = this._detectNavbar();
260
261
      if (this._popper !== null) {
262
        this._popper.scheduleUpdate();
263
      }
264
    } // Private
265
    ;
266
267
    _proto._addEventListeners = function _addEventListeners() {
268
      var _this = this;
269
270
      $__default["default"](this._element).on(EVENT_CLICK, function (event) {
271
        event.preventDefault();
272
        event.stopPropagation();
273
274
        _this.toggle();
275
      });
276
    };
277
278
    _proto._getConfig = function _getConfig(config) {
279
      config = _extends({}, this.constructor.Default, $__default["default"](this._element).data(), config);
280
      Util__default["default"].typeCheckConfig(NAME, config, this.constructor.DefaultType);
281
      return config;
282
    };
283
284
    _proto._getMenuElement = function _getMenuElement() {
285
      if (!this._menu) {
286
        var parent = Dropdown._getParentFromElement(this._element);
287
288
        if (parent) {
289
          this._menu = parent.querySelector(SELECTOR_MENU);
290
        }
291
      }
292
293
      return this._menu;
294
    };
295
296
    _proto._getPlacement = function _getPlacement() {
297
      var $parentDropdown = $__default["default"](this._element.parentNode);
298
      var placement = PLACEMENT_BOTTOM; // Handle dropup
299
300
      if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) {
301
        placement = $__default["default"](this._menu).hasClass(CLASS_NAME_MENURIGHT) ? PLACEMENT_TOPEND : PLACEMENT_TOP;
302
      } else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) {
303
        placement = PLACEMENT_RIGHT;
304
      } else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) {
305
        placement = PLACEMENT_LEFT;
306
      } else if ($__default["default"](this._menu).hasClass(CLASS_NAME_MENURIGHT)) {
307
        placement = PLACEMENT_BOTTOMEND;
308
      }
309
310
      return placement;
311
    };
312
313
    _proto._detectNavbar = function _detectNavbar() {
314
      return $__default["default"](this._element).closest('.navbar').length > 0;
315
    };
316
317
    _proto._getOffset = function _getOffset() {
318
      var _this2 = this;
319
320
      var offset = {};
321
322
      if (typeof this._config.offset === 'function') {
323
        offset.fn = function (data) {
324
          data.offsets = _extends({}, data.offsets, _this2._config.offset(data.offsets, _this2._element));
325
          return data;
326
        };
327
      } else {
328
        offset.offset = this._config.offset;
329
      }
330
331
      return offset;
332
    };
333
334
    _proto._getPopperConfig = function _getPopperConfig() {
335
      var popperConfig = {
336
        placement: this._getPlacement(),
337
        modifiers: {
338
          offset: this._getOffset(),
339
          flip: {
340
            enabled: this._config.flip
341
          },
342
          preventOverflow: {
343
            boundariesElement: this._config.boundary
344
          }
345
        }
346
      }; // Disable Popper if we have a static display
347
348
      if (this._config.display === 'static') {
349
        popperConfig.modifiers.applyStyle = {
350
          enabled: false
351
        };
352
      }
353
354
      return _extends({}, popperConfig, this._config.popperConfig);
355
    } // Static
356
    ;
357
358
    Dropdown._jQueryInterface = function _jQueryInterface(config) {
359
      return this.each(function () {
360
        var data = $__default["default"](this).data(DATA_KEY);
361
362
        var _config = typeof config === 'object' ? config : null;
363
364
        if (!data) {
365
          data = new Dropdown(this, _config);
366
          $__default["default"](this).data(DATA_KEY, data);
367
        }
368
369
        if (typeof config === 'string') {
370
          if (typeof data[config] === 'undefined') {
371
            throw new TypeError("No method named \"" + config + "\"");
372
          }
373
374
          data[config]();
375
        }
376
      });
377
    };
378
379
    Dropdown._clearMenus = function _clearMenus(event) {
380
      if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
381
        return;
382
      }
383
384
      var toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE));
385
386
      for (var i = 0, len = toggles.length; i < len; i++) {
387
        var parent = Dropdown._getParentFromElement(toggles[i]);
388
389
        var context = $__default["default"](toggles[i]).data(DATA_KEY);
390
        var relatedTarget = {
391
          relatedTarget: toggles[i]
392
        };
393
394
        if (event && event.type === 'click') {
395
          relatedTarget.clickEvent = event;
396
        }
397
398
        if (!context) {
399
          continue;
400
        }
401
402
        var dropdownMenu = context._menu;
403
404
        if (!$__default["default"](parent).hasClass(CLASS_NAME_SHOW)) {
405
          continue;
406
        }
407
408
        if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $__default["default"].contains(parent, event.target)) {
409
          continue;
410
        }
411
412
        var hideEvent = $__default["default"].Event(EVENT_HIDE, relatedTarget);
413
        $__default["default"](parent).trigger(hideEvent);
414
415
        if (hideEvent.isDefaultPrevented()) {
416
          continue;
417
        } // If this is a touch-enabled device we remove the extra
418
        // empty mouseover listeners we added for iOS support
419
420
421
        if ('ontouchstart' in document.documentElement) {
422
          $__default["default"](document.body).children().off('mouseover', null, $__default["default"].noop);
423
        }
424
425
        toggles[i].setAttribute('aria-expanded', 'false');
426
427
        if (context._popper) {
428
          context._popper.destroy();
429
        }
430
431
        $__default["default"](dropdownMenu).removeClass(CLASS_NAME_SHOW);
432
        $__default["default"](parent).removeClass(CLASS_NAME_SHOW).trigger($__default["default"].Event(EVENT_HIDDEN, relatedTarget));
433
      }
434
    };
435
436
    Dropdown._getParentFromElement = function _getParentFromElement(element) {
437
      var parent;
438
      var selector = Util__default["default"].getSelectorFromElement(element);
439
440
      if (selector) {
441
        parent = document.querySelector(selector);
442
      }
443
444
      return parent || element.parentNode;
445
    } // eslint-disable-next-line complexity
446
    ;
447
448
    Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) {
449
      // If not input/textarea:
450
      //  - And not a key in REGEXP_KEYDOWN => not a dropdown command
451
      // If input/textarea:
452
      //  - If space key => not a dropdown command
453
      //  - If key is other than escape
454
      //    - If key is not up or down => not a dropdown command
455
      //    - If trigger inside the menu => not a dropdown command
456
      if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $__default["default"](event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
457
        return;
458
      }
459
460
      if (this.disabled || $__default["default"](this).hasClass(CLASS_NAME_DISABLED)) {
461
        return;
462
      }
463
464
      var parent = Dropdown._getParentFromElement(this);
465
466
      var isActive = $__default["default"](parent).hasClass(CLASS_NAME_SHOW);
467
468
      if (!isActive && event.which === ESCAPE_KEYCODE) {
469
        return;
470
      }
471
472
      event.preventDefault();
473
      event.stopPropagation();
474
475
      if (!isActive || event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE) {
476
        if (event.which === ESCAPE_KEYCODE) {
477
          $__default["default"](parent.querySelector(SELECTOR_DATA_TOGGLE)).trigger('focus');
478
        }
479
480
        $__default["default"](this).trigger('click');
481
        return;
482
      }
483
484
      var items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS)).filter(function (item) {
485
        return $__default["default"](item).is(':visible');
486
      });
487
488
      if (items.length === 0) {
489
        return;
490
      }
491
492
      var index = items.indexOf(event.target);
493
494
      if (event.which === ARROW_UP_KEYCODE && index > 0) {
495
        // Up
496
        index--;
497
      }
498
499
      if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) {
500
        // Down
501
        index++;
502
      }
503
504
      if (index < 0) {
505
        index = 0;
506
      }
507
508
      items[index].focus();
509
    };
510
511
    _createClass(Dropdown, null, [{
512
      key: "VERSION",
513
      get: function get() {
514
        return VERSION;
515
      }
516
    }, {
517
      key: "Default",
518
      get: function get() {
519
        return Default;
520
      }
521
    }, {
522
      key: "DefaultType",
523
      get: function get() {
524
        return DefaultType;
525
      }
526
    }]);
527
528
    return Dropdown;
529
  }();
530
  /**
531
   * Data API implementation
532
   */
533
534
535
  $__default["default"](document).on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler).on(EVENT_CLICK_DATA_API + " " + EVENT_KEYUP_DATA_API, Dropdown._clearMenus).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
536
    event.preventDefault();
537
    event.stopPropagation();
538
539
    Dropdown._jQueryInterface.call($__default["default"](this), 'toggle');
540
  }).on(EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, function (e) {
541
    e.stopPropagation();
542
  });
543
  /**
544
   * jQuery
545
   */
546
547
  $__default["default"].fn[NAME] = Dropdown._jQueryInterface;
548
  $__default["default"].fn[NAME].Constructor = Dropdown;
549
550
  $__default["default"].fn[NAME].noConflict = function () {
551
    $__default["default"].fn[NAME] = JQUERY_NO_CONFLICT;
552
    return Dropdown._jQueryInterface;
553
  };
554
555
  return Dropdown;
556
557
}));
558
//# sourceMappingURL=dropdown.js.map
559